package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"strings"
)

// Project 定义项目结构体，用于解析从 Harbor 获取的项目信息
type Project struct {
	ProjectID    int    `json:"project_id"`
	OwnerID      int    `json:"owner_id"`
	Name         string `json:"name"`
	CreationTime string `json:"creation_time"`
	UpdateTime   string `json:"update_time"`
	Deleted      bool   `json:"deleted"`
	OwnerName    string `json:"owner_name"`
	Togglable    bool   `json:"togglable"`
	RepoCount    int    `json:"repo_count"`
	ChartCount   int    `json:"chart_count"`
	Metadata     struct {
		Public string `json:"public"`
	} `json:"metadata"`
}

// Config 定义配置结构体，用于存储从环境变量或配置文件中读取的配置信息
type Config struct {
	OldHarborURL        string
	OldHarborAuthHeader string
	NewHarborURL        string
	NewHarborAuthHeader string
	Mode                string
	Filename            string
}

// loadConfig 函数用于加载配置，优先从环境变量读取，若环境变量未设置则尝试从配置文件读取
func loadConfig(configFilePath string) (Config, error) {
	var config Config

	// 从环境变量读取配置
	config.OldHarborURL = os.Getenv("OLD_HARBOR_URL")
	config.OldHarborAuthHeader = os.Getenv("OLD_HARBOR_AUTH_HEADER")
	config.NewHarborURL = os.Getenv("NEW_HARBOR_URL")
	config.NewHarborAuthHeader = os.Getenv("NEW_HARBOR_AUTH_HEADER")
	config.Mode = strings.ToLower(os.Getenv("MODE"))
	config.Filename = os.Getenv("FILENAME")
	if config.Filename == "" {
		config.Filename = "harbor_projects.json"
	}

	// 如果环境变量未设置，尝试从配置文件读取
	if config.OldHarborURL == "" || config.OldHarborAuthHeader == "" || config.NewHarborURL == "" || config.NewHarborAuthHeader == "" || config.Mode == "" {
		if configFilePath == "" {
			return config, fmt.Errorf("some environment variables are not set and no config file path provided")
		}
		fileContent, err := os.ReadFile(configFilePath)
		if err != nil {
			return config, fmt.Errorf("failed to read config file: %w", err)
		}
		err = json.Unmarshal(fileContent, &config)
		if err != nil {
			return config, fmt.Errorf("failed to parse config file: %w", err)
		}
	}

	// 检查必要的配置项是否都已设置
	if config.OldHarborURL == "" || config.OldHarborAuthHeader == "" || config.NewHarborURL == "" || config.NewHarborAuthHeader == "" || config.Mode == "" {
		return config, fmt.Errorf("some required configurations are missing")
	}

	return config, nil
}

// exportProjects 函数用于从 Harbor 导出项目列表
func exportProjects(url, authHeader string, pageSize int) ([]Project, error) {
	var allProjects []Project
	page := 1

	for {
		// 构造当前页的查询 URL
		currentURL := fmt.Sprintf("%s?page=%d&page_size=%d&with_detail=true", url, page, pageSize)
		req, err := http.NewRequest("GET", currentURL, nil)
		if err != nil {
			return nil, err
		}
		req.Header.Set("accept", "application/json")
		req.Header.Set("authorization", authHeader)

		client := &http.Client{}
		resp, err := client.Do(req)
		if err != nil {
			return nil, err
		}
		defer resp.Body.Close()

		if resp.StatusCode != http.StatusOK {
			body, _ := io.ReadAll(resp.Body)
			return nil, fmt.Errorf("request failed with status code %d: %s", resp.StatusCode, string(body))
		}

		body, err := io.ReadAll(resp.Body)
		if err != nil {
			return nil, err
		}

		var projects []Project
		err = json.Unmarshal(body, &projects)
		if err != nil {
			return nil, err
		}

		// 如果当前页没有数据，说明已经获取完所有数据，退出循环
		if len(projects) == 0 {
			break
		}

		allProjects = append(allProjects, projects...)
		page++
	}

	return allProjects, nil
}

// saveProjectsToFile 函数用于将导出的项目信息保存到文件
func saveProjectsToFile(projects []Project, filename string) error {
	data, err := json.MarshalIndent(projects, "", "  ")
	if err != nil {
		return err
	}

	file, err := os.Create(filename)
	if err != nil {
		return err
	}
	defer file.Close()

	_, err = file.Write(data)
	if err != nil {
		return err
	}

	return nil
}

// createProject 函数用于向新的 Harbor 实例创建项目
func createProject(url, authHeader string, project Project) error {
	requestBody := map[string]interface{}{
		"project_name": project.Name,
		"public":       project.Metadata.Public == "true",
	}
	jsonBody, err := json.Marshal(requestBody)
	if err != nil {
		return err
	}

	req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonBody))
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("authorization", authHeader)

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusCreated {
		body, _ := io.ReadAll(resp.Body)
		return fmt.Errorf("failed to create project %s: status code %d, body: %s", project.Name, resp.StatusCode, string(body))
	}

	return nil
}

// importProjectsFromFile 函数用于从文件读取项目信息并导入到新的 Harbor
func importProjectsFromFile(newHarborURL, newHarborAuthHeader, filename string) error {
	fileContent, err := os.ReadFile(filename)
	if err != nil {
		return fmt.Errorf("error reading file: %w", err)
	}

	var projects []Project
	err = json.Unmarshal(fileContent, &projects)
	if err != nil {
		return fmt.Errorf("error parsing JSON: %w", err)
	}

	for _, project := range projects {
		err := createProject(newHarborURL, newHarborAuthHeader, project)
		if err != nil {
			fmt.Printf("Error creating project %s: %v\n", project.Name, err)
		} else {
			fmt.Printf("Project %s created successfully\n", project.Name)
		}
	}

	return nil
}

func main() {
	var configFilePath string
	if len(os.Args) > 1 {
		configFilePath = os.Args[1]
	}

	config, err := loadConfig(configFilePath)
	if err != nil {
		fmt.Printf("Error loading config: %v\n", err)
		return
	}

	switch config.Mode {
	case "export":
		// 导出项目列表，设置每页返回 20 条记录
		projects, err := exportProjects(config.OldHarborURL, config.OldHarborAuthHeader, 20)
		if err != nil {
			fmt.Printf("Error exporting projects: %v\n", err)
			return
		}

		// 保存项目信息到文件
		err = saveProjectsToFile(projects, config.Filename)
		if err != nil {
			fmt.Printf("Error saving projects to file: %v\n", err)
			return
		}
		fmt.Printf("Projects exported successfully to %s\n", config.Filename)
	case "import":
		// 从文件导入项目到新 Harbor
		err = importProjectsFromFile(config.NewHarborURL, config.NewHarborAuthHeader, config.Filename)
		if err != nil {
			fmt.Printf("Error importing projects: %v\n", err)
			return
		}
		fmt.Println("Projects imported successfully to new Harbor")
	default:
		fmt.Println("Invalid MODE value. Allowed values are 'export' or 'import'.")
	}
}
